﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Hive.Core.Common.Shell;
using ICSharpCode.SharpZipLib.Zip;

namespace Hive.Core.Model
{
    /// <summary>
    /// An archived binary artifact is a ZIP file of package exports.
    /// </summary>
    public class ArchivedBinaryArtifact : IBinaryArtifact
    {
        private readonly ArchivedBinaryRepository repository;
        private readonly FileInfo file;
        private readonly string packageName;
        private readonly string branchName;
        private readonly string versionLabel;

        /// <summary>
        /// Creates an archived binary artifact.
        /// </summary>
        /// <param name="repository">The archived binary repository.</param>
        /// <param name="file">The archive file.</param>
        /// <param name="packageName">The package name.</param>
        /// <param name="branchName">The branch name.</param>
        /// <param name="versionLabel">The version label.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="repository"/>, <paramref name="file"/>,
        /// <paramref name="packageName"/>, <paramref name="branchName"/> or <paramref name="versionLabel"/> is null.</exception>
        public ArchivedBinaryArtifact(ArchivedBinaryRepository repository, FileInfo file, string packageName, string branchName, string versionLabel)
        {
            if (repository == null)
                throw new ArgumentNullException("repository");
            if (file == null)
                throw new ArgumentNullException("file");
            if (packageName == null)
                throw new ArgumentNullException("packageName");
            if (branchName == null)
                throw new ArgumentNullException("branchName");
            if (versionLabel == null)
                throw new ArgumentNullException("versionLabel");

            this.repository = repository;
            this.file = file;
            this.packageName = packageName;
            this.branchName = branchName;
            this.versionLabel = versionLabel;
        }

        /// <inheritdoc />
        public IBinaryRepository Repository
        {
            get { return repository; }
        }

        /// <inheritdoc />
        public string PackageName
        {
            get { return packageName; }
        }

        /// <inheritdoc />
        public string BranchName
        {
            get { return branchName; }
        }

        /// <inheritdoc />
        public string VersionLabel
        {
            get { return versionLabel; }
        }

        /// <inheritdoc />
        public DateTime CreationTimeUtc
        {
            get { return file.CreationTimeUtc; }
        }

        /// <inheritdoc />
        public void Extract(DirectoryInfo destinationDirectory, IShell shell)
        {
            if (destinationDirectory == null)
                throw new ArgumentNullException("destinationDirectory");
            if (shell == null)
                throw new ArgumentNullException("shell");

            try
            {
                bool error = false;
                var zipEvents = new FastZipEvents();

                zipEvents.DirectoryFailure += (sender, e) =>
                {
                    shell.Echo(MessageSeverity.Error,
                        string.Format("Failed to extract directory '{0}' from archive '{1}'.\n{2}", e.Name,
                            file.FullName, e.Exception));
                    error = true;
                };

                zipEvents.FileFailure += (sender, e) =>
                {
                    shell.Echo(MessageSeverity.Error,
                        string.Format("Failed to extract file '{0}' from archive '{1}'.\n{2}", e.Name, file.FullName,
                            e.Exception));
                    error = true;
                };

                if (!destinationDirectory.Exists)
                    destinationDirectory.Create();

                var zip = new FastZip(zipEvents);
                zip.ExtractZip(file.FullName, destinationDirectory.FullName, FastZip.Overwrite.Always, null, null, null, false);

                if (error)
                    throw new BinaryRepositoryException(string.Format("Failed to extract archive '{0}'.", file.FullName));
            }
            catch (BinaryRepositoryException)
            {
                throw;
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new BinaryRepositoryException(string.Format("Failed to extract archive '{0}'.", file.FullName), ex);
            }
        }
    }
}
